use tokio::sync::oneshot;
use tonic::transport::Channel;

use super::rauth_proto::{self, rauth_client::RauthClient};
use crate::types::{self, channel};

pub struct Client {
    client: RauthClient<Channel>,
}

impl Client {
    pub async fn new() -> Self {
        // server is a micro serviec: rauth
        // port is 9001, and k8s dns will handle this domain
        // "http" is necessary
        let rauth_channel = Channel::from_static("http://rauth:9001")
            .connect()
            .await
            .expect("failed to connect to grcp server in rauth");

        println!("Connect to rauth grpc server successfully");

        Client {
            client: RauthClient::new(rauth_channel),
        }
    }

    pub async fn user_register(
        &mut self,
        request: types::user::RegisterRequest,
        rsp_ch: oneshot::Sender<channel::Response>,
    ) {
        println!("Grpc client => User register: {:#?}", &request);
        match self
            .client
            .register(rauth_proto::RegisterInfo {
                username: request.username,
                email: request.email,
                password: request.password,
            })
            .await
        {
            Ok(_) => {
                rsp_ch
                    .send(channel::Response::Ok())
                    .expect("Grpc send register response failed");
            }
            Err(status) => rsp_ch
                .send(channel::Response::Error(status.to_string()))
                .expect("Grpc send register response failed"),
        }
    }

    pub async fn user_login(
        &mut self,
        request: types::user::LoginRequest,
        rsp_ch: oneshot::Sender<channel::Response>,
    ) {
        match self
            .client
            .login(rauth_proto::LoginInfo {
                email: request.email,
                password: request.password,
            })
            .await
        {
            Ok(rsp) => {
                let login_rsp = rsp.into_inner();
                println!("login_rsp: {:?}", &login_rsp);
                let user_info = login_rsp.user_info.unwrap();

                // set token
                rsp_ch
                    .send(channel::Response::UserLogin(types::user::LoginResponse {
                        username: user_info.username,
                        user_id: user_info.user_id,
                        level: user_info.level as u8,
                        new_token: login_rsp.new_token,
                        token_expiration: login_rsp.token_expiration,
                    }))
                    .expect("Grpc send login response failed");
            }
            Err(status) => rsp_ch
                .send(channel::Response::Error(status.to_string()))
                .expect("Grpc send login response failed"),
        }
    }

    pub async fn user_change_password(
        &mut self,
        request: types::user::ChangePasswordRequest,
        rsp_ch: oneshot::Sender<channel::Response>,
    ) {
        match self
            .client
            .user_change_password(rauth_proto::ChangePasswordInfo {
                user_id: request.user_id,
                old_password: request.old_password,
                new_password: request.new_password,
            })
            .await
        {
            Ok(_) => {
                rsp_ch
                    .send(channel::Response::Ok())
                    .expect("Grpc send change-password response failed");
            }
            Err(status) => rsp_ch
                .send(channel::Response::Error(status.to_string()))
                .expect("Grpc send change-password response failed"),
        }
    }

    pub async fn user_update(
        &mut self,
        request: types::user::UserInfo,
        rsp_ch: oneshot::Sender<channel::Response>,
    ) {
        match self
            .client
            .user_update(rauth_proto::UserInfo {
                user_id: request.user_id,
                username: request.username,
                email: request.email,
                level: request.level as i32,
                birthday: request.birthday,
                created_at: request.created_at,
            })
            .await
        {
            Ok(_) => {
                rsp_ch
                    .send(channel::Response::Ok())
                    .expect("Grpc send user-update response failed");
            }
            Err(status) => rsp_ch
                .send(channel::Response::Error(status.to_string()))
                .expect("Grpc send user-update response failed"),
        }
    }

    pub async fn user_get(&mut self, user_id: String, rsp_ch: oneshot::Sender<channel::Response>) {
        match self.client.user_get(rauth_proto::UserId { user_id }).await {
            Ok(rsp) => {
                let user_info = rsp.into_inner();
                rsp_ch
                    .send(channel::Response::UserGet(types::user::UserInfo {
                        user_id: user_info.user_id,
                        username: user_info.username,
                        email: user_info.email,
                        level: user_info.level as u8,
                        birthday: user_info.birthday,
                        created_at: user_info.created_at,
                    }))
                    .expect("Grpc send user-get response failed");
            }
            Err(status) => rsp_ch
                .send(channel::Response::Error(status.to_string()))
                .expect("Grpc send user-get response failed"),
        }
    }

    pub async fn user_delete(
        &mut self,
        user_id: String,
        rsp_ch: oneshot::Sender<channel::Response>,
    ) {
        match self
            .client
            .user_delete(rauth_proto::UserId { user_id })
            .await
        {
            Ok(_) => {
                rsp_ch
                    .send(channel::Response::Ok())
                    .expect("Grpc send user-delete response failed");
            }
            Err(status) => rsp_ch
                .send(channel::Response::Error(status.to_string()))
                .expect("Grpc send user-delete response failed"),
        }
    }
}
